home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / MAME / src / datafile.c < prev    next >
C/C++ Source or Header  |  2000-04-25  |  15KB  |  598 lines

  1. /****************************************************************************
  2.  *    datafile.c
  3.  *    History database engine
  4.  *
  5.  *    Token parsing by Neil Bradley
  6.  *    Modifications and higher-level functions by John Butler
  7.  ****************************************************************************/
  8.  
  9. #include <assert.h>
  10. #include <ctype.h>
  11. #include "osd_cpu.h"
  12. #include "driver.h"
  13. #include "datafile.h"
  14.  
  15.  
  16. /****************************************************************************
  17.  *    token parsing constants
  18.  ****************************************************************************/
  19. #ifndef TRUE
  20. #define TRUE 1
  21. #endif
  22.  
  23. #ifndef FALSE
  24. #define FALSE 0
  25. #endif
  26.  
  27. #define CR    0x0d    /* '\n' and '\r' meanings are swapped in some */
  28. #define LF    0x0a    /*     compilers (e.g., Mac compilers) */
  29.  
  30. enum
  31. {
  32.     TOKEN_COMMA,
  33.     TOKEN_EQUALS,
  34.     TOKEN_SYMBOL,
  35.     TOKEN_LINEBREAK,
  36.     TOKEN_INVALID=-1
  37. };
  38.  
  39. #define    MAX_TOKEN_LENGTH    256
  40.  
  41.  
  42. /****************************************************************************
  43.  *    datafile constants
  44.  ****************************************************************************/
  45. #define MAX_DATAFILE_ENTRIES 1275
  46. #define DATAFILE_TAG '$'
  47.  
  48. const char *DATAFILE_TAG_KEY = "$info";
  49. const char *DATAFILE_TAG_BIO = "$bio";
  50. const char *DATAFILE_TAG_MAME = "$mame";
  51.  
  52. char *history_filename = "history.dat";
  53. char *mameinfo_filename = "mameinfo.dat";
  54.  
  55.  
  56. /****************************************************************************
  57.  *    private data for parsing functions
  58.  ****************************************************************************/
  59. static void *fp;                            /* Our file pointer */
  60. static long dwFilePos;                        /* file position */
  61. static UINT8 bToken[MAX_TOKEN_LENGTH];        /* Our current token */
  62.  
  63.  
  64.  
  65. /**************************************************************************
  66.  **************************************************************************
  67.  *
  68.  *        Parsing functions
  69.  *
  70.  **************************************************************************
  71.  **************************************************************************/
  72.  
  73. /****************************************************************************
  74.  *    GetNextToken - Pointer to the token string pointer
  75.  *                   Pointer to position within file
  76.  *
  77.  *    Returns token, or TOKEN_INVALID if at end of file
  78.  ****************************************************************************/
  79. static UINT32 GetNextToken(UINT8 **ppszTokenText, long *pdwPosition)
  80. {
  81.     UINT32 dwLength;                        /* Length of symbol */
  82.     long dwPos;                                /* Temporary position */
  83.     UINT8 *pbTokenPtr = bToken;                /* Point to the beginning */
  84.     UINT8 bData;                            /* Temporary data byte */
  85.  
  86.     while (1)
  87.     {
  88.         bData = osd_fgetc(fp);                    /* Get next character */
  89.  
  90.         /* If we're at the end of the file, bail out */
  91.  
  92.         if (osd_feof(fp))
  93.             return(TOKEN_INVALID);
  94.  
  95.         /* If it's not whitespace, then let's start eating characters */
  96.  
  97.         if (' ' != bData && '\t' != bData)
  98.         {
  99.             /* Store away our file position (if given on input) */
  100.  
  101.             if (pdwPosition)
  102.                 *pdwPosition = dwFilePos;
  103.  
  104.             /* If it's a separator, special case it */
  105.  
  106.             if (',' == bData || '=' == bData)
  107.             {
  108.                 *pbTokenPtr++ = bData;
  109.                 *pbTokenPtr = '\0';
  110.                 ++dwFilePos;
  111.  
  112.                 if (',' == bData)
  113.                     return(TOKEN_COMMA);
  114.                 else
  115.                     return(TOKEN_EQUALS);
  116.             }
  117.  
  118.             /* Otherwise, let's try for a symbol */
  119.  
  120.             if (bData > ' ')
  121.             {
  122.                 dwLength = 0;            /* Assume we're 0 length to start with */
  123.  
  124.                 /* Loop until we've hit something we don't understand */
  125.  
  126.                 while (bData != ',' &&
  127.                          bData != '=' &&
  128.                          bData != ' ' &&
  129.                          bData != '\t' &&
  130.                          bData != '\n' &&
  131.                          bData != '\r' &&
  132.                          osd_feof(fp) == 0)
  133.                 {
  134.                     ++dwFilePos;
  135.                     *pbTokenPtr++ = bData;    /* Store our byte */
  136.                     ++dwLength;
  137.                     assert(dwLength < MAX_TOKEN_LENGTH);
  138.                     bData = osd_fgetc(fp);
  139.                 }
  140.  
  141.                 /* If it's not the end of the file, put the last received byte */
  142.                 /* back. We don't want to touch the file position, though if */
  143.                 /* we're past the end of the file. Otherwise, adjust it. */
  144.  
  145.                 if (0 == osd_feof(fp))
  146.                 {
  147.                     osd_ungetc(bData, fp);
  148.                 }
  149.  
  150.                 /* Null terminate the token */
  151.  
  152.                 *pbTokenPtr = '\0';
  153.  
  154.                 /* Connect up the */
  155.  
  156.                 if (ppszTokenText)
  157.                     *ppszTokenText = bToken;
  158.  
  159.                 return(TOKEN_SYMBOL);
  160.             }
  161.  
  162.             /* Not a symbol. Let's see if it's a cr/cr, lf/lf, or cr/lf/cr/lf */
  163.             /* sequence */
  164.  
  165.             if (LF == bData)
  166.             {
  167.                 /* Unix style perhaps? */
  168.  
  169.                 bData = osd_fgetc(fp);        /* Peek ahead */
  170.                 osd_ungetc(bData, fp);        /* Force a retrigger if subsequent LF's */
  171.  
  172.                 if (LF == bData)        /* Two LF's in a row - it's a UNIX hard CR */
  173.                 {
  174.                     ++dwFilePos;
  175.                     *pbTokenPtr++ = bData;    /* A real linefeed */
  176.                     *pbTokenPtr = '\0';
  177.                     return(TOKEN_LINEBREAK);
  178.                 }
  179.  
  180.                 /* Otherwise, fall through and keep parsing. */
  181.  
  182.             }
  183.             else
  184.             if (CR == bData)        /* Carriage return? */
  185.             {
  186.                 /* Figure out if it's Mac or MSDOS format */
  187.  
  188.                 ++dwFilePos;
  189.                 bData = osd_fgetc(fp);        /* Peek ahead */
  190.  
  191.                 /* We don't need to bother with EOF checking. It will be 0xff if */
  192.                 /* it's the end of the file and will be caught by the outer loop. */
  193.  
  194.                 if (CR == bData)        /* Mac style hard return! */
  195.                 {
  196.                     /* Do not advance the file pointer in case there are successive */
  197.                     /* CR/CR sequences */
  198.  
  199.                     /* Stuff our character back upstream for successive CR's */
  200.  
  201.                     osd_ungetc(bData, fp);
  202.  
  203.                     *pbTokenPtr++ = bData;    /* A real carriage return (hard) */
  204.                     *pbTokenPtr = '\0';
  205.                     return(TOKEN_LINEBREAK);
  206.                 }
  207.                 else
  208.                 if (LF == bData)    /* MSDOS format! */
  209.                 {
  210.                     ++dwFilePos;            /* Our file position to reset to */
  211.                     dwPos = dwFilePos;        /* Here so we can reposition things */
  212.  
  213.                     /* Look for a followup CR/LF */
  214.  
  215.                     bData = osd_fgetc(fp);    /* Get the next byte */
  216.  
  217.                     if (CR == bData)    /* CR! Good! */
  218.                     {
  219.                         bData = osd_fgetc(fp);    /* Get the next byte */
  220.  
  221.                         /* We need to do this to pick up subsequent CR/LF sequences */
  222.  
  223.                         osd_fseek(fp, dwPos, SEEK_SET);
  224.  
  225.                         if (pdwPosition)
  226.                             *pdwPosition = dwPos;
  227.  
  228.                         if (LF == bData)    /* LF? Good! */
  229.                         {
  230.                             *pbTokenPtr++ = '\r';
  231.                             *pbTokenPtr++ = '\n';
  232.                             *pbTokenPtr = '\0';
  233.  
  234.                             return(TOKEN_LINEBREAK);
  235.                         }
  236.                     }
  237.                     else
  238.                     {
  239.                         --dwFilePos;
  240.                         osd_ungetc(bData, fp);    /* Put the character back. No good */
  241.                     }
  242.                 }
  243.                 else
  244.                 {
  245.                     --dwFilePos;
  246.                     osd_ungetc(bData, fp);    /* Put the character back. No good */
  247.                 }
  248.  
  249.                 /* Otherwise, fall through and keep parsing */
  250.             }
  251.         }
  252.  
  253.         ++dwFilePos;
  254.     }
  255. }
  256.  
  257.  
  258. /****************************************************************************
  259.  *    ParseClose - Closes the existing opened file (if any)
  260.  ****************************************************************************/
  261. static void ParseClose(void)
  262. {
  263.     /* If the file is open, time for fclose. */
  264.  
  265.     if (fp)
  266.     {
  267.         osd_fclose(fp);
  268.     }
  269.  
  270.     fp = NULL;
  271. }
  272.  
  273.  
  274. /****************************************************************************
  275.  *    ParseOpen - Open up file for reading
  276.  ****************************************************************************/
  277. static UINT8 ParseOpen(const char *pszFilename)
  278. {
  279.     /* Open file up in binary mode */
  280.  
  281.     fp = osd_fopen (NULL, pszFilename, OSD_FILETYPE_HISTORY, 0);
  282.  
  283.     /* If this is NULL, return FALSE. We can't open it */
  284.  
  285.     if (NULL == fp)
  286.     {
  287.         return(FALSE);
  288.     }
  289.  
  290.     /* Otherwise, prepare! */
  291.  
  292.     dwFilePos = 0;
  293.     return(TRUE);
  294. }
  295.  
  296.  
  297. /****************************************************************************
  298.  *    ParseSeek - Move the file position indicator
  299.  ****************************************************************************/
  300. static UINT8 ParseSeek(long offset, int whence)
  301. {
  302.     int result = osd_fseek(fp, offset, whence);
  303.  
  304.     if (0 == result)
  305.     {
  306.         dwFilePos = osd_ftell(fp);
  307.     }
  308.     return (UINT8)result;
  309. }
  310.  
  311.  
  312.  
  313. /**************************************************************************
  314.  **************************************************************************
  315.  *
  316.  *        Datafile functions
  317.  *
  318.  **************************************************************************
  319.  **************************************************************************/
  320.  
  321.  
  322. /**************************************************************************
  323.  *    ci_strcmp - case insensitive string compare
  324.  *
  325.  *    Returns zero if s1 and s2 are equal, ignoring case
  326.  **************************************************************************/
  327. static int ci_strcmp (const char *s1, const char *s2)
  328. {
  329.     int c1, c2;
  330.  
  331.     while ((c1 = tolower(*s1++)) == (c2 = tolower(*s2++)))
  332.         if (!c1)
  333.             return 0;
  334.  
  335.     return (c1 - c2);
  336. }
  337.  
  338.  
  339. /**************************************************************************
  340.  *    ci_strncmp - case insensitive character array compare
  341.  *
  342.  *    Returns zero if the first n characters of s1 and s2 are equal,
  343.  *    ignoring case.
  344.  **************************************************************************/
  345. static int ci_strncmp (const char *s1, const char *s2, int n)
  346. {
  347.     int c1, c2;
  348.  
  349.     while (n)
  350.     {
  351.         if ((c1 = tolower (*s1++)) != (c2 = tolower (*s2++)))
  352.             return (c1 - c2);
  353.         else if (!c1)
  354.             break;
  355.         --n;
  356.     }
  357.     return 0;
  358. }
  359.  
  360.  
  361. /**************************************************************************
  362.  *    index_datafile
  363.  *    Create an index for the records in the currently open datafile.
  364.  *
  365.  *    Returns 0 on error, or the number of index entries created.
  366.  **************************************************************************/
  367. static int index_datafile (struct tDatafileIndex **_index)
  368. {
  369.     struct tDatafileIndex *idx;
  370.     int count = 0;
  371.     UINT32 token = TOKEN_SYMBOL;
  372.  
  373.     /* rewind file */
  374.     if (ParseSeek (0L, SEEK_SET)) return 0;
  375.  
  376.     /* allocate index */
  377.     idx = *_index = malloc (MAX_DATAFILE_ENTRIES * sizeof (struct tDatafileIndex));
  378.     if (NULL == idx) return 0;
  379.  
  380.     /* loop through datafile */
  381.     while ((count < (MAX_DATAFILE_ENTRIES - 1)) && TOKEN_INVALID != token)
  382.     {
  383.         long tell;
  384.         char *s;
  385.  
  386.         token = GetNextToken ((UINT8 **)&s, &tell);
  387.         if (TOKEN_SYMBOL != token) continue;
  388.  
  389.         /* DATAFILE_TAG_KEY identifies the driver */
  390.         if (!ci_strncmp (DATAFILE_TAG_KEY, s, strlen (DATAFILE_TAG_KEY)))
  391.         {
  392.             token = GetNextToken ((UINT8 **)&s, &tell);
  393.             if (TOKEN_EQUALS == token)
  394.             {
  395.                 int done = 0;
  396.                 int    i;
  397.  
  398.                 token = GetNextToken ((UINT8 **)&s, &tell);
  399.                 while (!done && TOKEN_SYMBOL == token)
  400.                 {
  401.                     /* search for matching driver name */
  402.                     for (i = 0; drivers[i]; i++)
  403.                     {
  404.                         if (!ci_strcmp (s, drivers[i]->name))
  405.                         {
  406.                             /* found correct driver -- fill in index entry */
  407.                             idx->driver = drivers[i];
  408.                             idx->offset = tell;
  409.                             idx++;
  410.                             count++;
  411.                             done = 1;
  412.                             break;
  413.                         }
  414.                     }
  415.  
  416.                     if (!done)
  417.                     {
  418.                         token = GetNextToken ((UINT8 **)&s, &tell);
  419.                         if (TOKEN_COMMA == token)
  420.                             token = GetNextToken ((UINT8 **)&s, &tell);
  421.                         else
  422.                             done = 1; /* end of key field */
  423.                     }
  424.                 }
  425.             }
  426.         }
  427.     }
  428.  
  429.     /* mark end of index */
  430.     idx->offset = 0L;
  431.     idx->driver = 0;
  432.     return count;
  433. }
  434.  
  435.  
  436. /**************************************************************************
  437.  *    load_datafile_text
  438.  *
  439.  *    Loads text field for a driver into the buffer specified. Specify the
  440.  *    driver, a pointer to the buffer, the buffer size, the index created by
  441.  *    index_datafile(), and the desired text field (e.g., DATAFILE_TAG_BIO).
  442.  *
  443.  *    Returns 0 if successful.
  444.  **************************************************************************/
  445. static int load_datafile_text (const struct GameDriver *drv, char *buffer, int bufsize,
  446.     struct tDatafileIndex *idx, const char *tag)
  447. {
  448.     int    offset = 0;
  449.     int found = 0;
  450.     UINT32    token = TOKEN_SYMBOL;
  451.     UINT32     prev_token = TOKEN_SYMBOL;
  452.  
  453.     *buffer = '\0';
  454.  
  455.     /* find driver in datafile index */
  456.     while (idx->driver)
  457.     {
  458.         if (idx->driver == drv) break;
  459.         idx++;
  460.     }
  461.     if (idx->driver == 0) return 1;    /* driver not found in index */
  462.  
  463.     /* seek to correct point in datafile */
  464.     if (ParseSeek (idx->offset, SEEK_SET)) return 1;
  465.  
  466.     /* read text until buffer is full or end of entry is encountered */
  467.     while (TOKEN_INVALID != token)
  468.     {
  469.         char *s;
  470.         int len;
  471.         long tell;
  472.  
  473.         token = GetNextToken ((UINT8 **)&s, &tell);
  474.         if (TOKEN_INVALID == token) continue;
  475.  
  476.         if (found)
  477.         {
  478.             /* end entry when a tag is encountered */
  479.             if (TOKEN_SYMBOL == token && DATAFILE_TAG == s[0] && TOKEN_LINEBREAK == prev_token) break;
  480.  
  481.             prev_token = token;
  482.  
  483.             /* translate platform-specific linebreaks to '\n' */
  484.             if (TOKEN_LINEBREAK == token)
  485.                 strcpy (s, "\n");
  486.  
  487.             /* append a space to words */
  488.             if (TOKEN_LINEBREAK != token)
  489.                 strcat (s, " ");
  490.  
  491.             /* remove extraneous space before commas */
  492.             if (TOKEN_COMMA == token)
  493.             {
  494.                 --buffer;
  495.                 --offset;
  496.                 *buffer = '\0';
  497.             }
  498.  
  499.             /* add this word to the buffer */
  500.             len = strlen (s);
  501.             if ((len + offset) >= bufsize) break;
  502.             strcpy (buffer, s);
  503.             buffer += len;
  504.             offset += len;
  505.         }
  506.         else
  507.         {
  508.             if (TOKEN_SYMBOL == token)
  509.             {
  510.                 /* looking for requested tag */
  511.                 if (!ci_strncmp (tag, s, strlen (tag)))
  512.                     found = 1;
  513.                 else if (!ci_strncmp (DATAFILE_TAG_KEY, s, strlen (DATAFILE_TAG_KEY)))
  514.                     break; /* error: tag missing */
  515.             }
  516.         }
  517.     }
  518.     return (!found);
  519. }
  520.  
  521.  
  522. /**************************************************************************
  523.  *    load_driver_history
  524.  *    Load history text for the specified driver into the specified buffer.
  525.  *    Combines $bio field of HISTORY.DAT with $mame field of MAMEINFO.DAT.
  526.  *
  527.  *    Returns 0 if successful.
  528.  *
  529.  *    NOTE: For efficiency the indices are never freed (intentional leak).
  530.  **************************************************************************/
  531. int load_driver_history (const struct GameDriver *drv, char *buffer, int bufsize)
  532. {
  533.     static struct tDatafileIndex *hist_idx = 0;
  534.     static struct tDatafileIndex *mame_idx = 0;
  535.     int history = 0, mameinfo = 0;
  536.     int err;
  537.  
  538.     *buffer = 0;
  539.  
  540.     /* try to open history datafile */
  541.     if (ParseOpen (history_filename))
  542.     {
  543.         /* create index if necessary */
  544.         if (hist_idx)
  545.             history = 1;
  546.         else
  547.             history = (index_datafile (&hist_idx) != 0);
  548.  
  549.         /* load history text */
  550.         if (hist_idx)
  551.         {
  552.             const struct GameDriver *gdrv;
  553.  
  554.             gdrv = drv;
  555.             do
  556.             {
  557.                 err = load_datafile_text (gdrv, buffer, bufsize,
  558.                                           hist_idx, DATAFILE_TAG_BIO);
  559.                 gdrv = gdrv->clone_of;
  560.             } while (err && gdrv);
  561.  
  562.             if (err) history = 0;
  563.         }
  564.         ParseClose ();
  565.     }
  566.  
  567.     /* try to open mameinfo datafile */
  568.     if (ParseOpen (mameinfo_filename))
  569.     {
  570.         /* create index if necessary */
  571.         if (mame_idx)
  572.             mameinfo = 1;
  573.         else
  574.             mameinfo = (index_datafile (&mame_idx) != 0);
  575.  
  576.         /* load informational text (append) */
  577.         if (mame_idx)
  578.         {
  579.             int len = strlen (buffer);
  580.             const struct GameDriver *gdrv;
  581.  
  582.             gdrv = drv;
  583.             do
  584.             {
  585.                 err = load_datafile_text (gdrv, buffer+len, bufsize-len,
  586.                                           mame_idx, DATAFILE_TAG_MAME);
  587.                 gdrv = gdrv->clone_of;
  588.             } while (err && gdrv);
  589.  
  590.             if (err) mameinfo = 0;
  591.         }
  592.         ParseClose ();
  593.     }
  594.  
  595.     return (history == 0 && mameinfo == 0);
  596. }
  597.  
  598.